import 'dart:io';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_mlkit_pose_detection/google_mlkit_pose_detection.dart';
import 'package:mediapipe_task_vision_example/main.dart';
import 'package:mediapipe_task_vision_example/page/p2p_connect.dart';
import 'package:mediapipe_task_vision_example/painter/PosePainter.dart';
import 'package:mediapipe_task_vision_example/footstep/footstep.dart';

class PoseMlKitWidget extends StatefulWidget {
  final query;
  const PoseMlKitWidget({super.key, this.query});

  @override
  State<PoseMlKitWidget> createState() => _PoseLiveWidgetState();
}

class _PoseLiveWidgetState extends State<PoseMlKitWidget> {
  late CameraController controller;
  var currentStep = StepType.unknown;
  bool isFrontCamera = true;
  P2PConnection? p2pConnection;
  StepAdapter? stepAdapter;
  PoseDetector? poseDetector;
  var camera;
  Map<PoseLandmarkType, PoseLandmark>? landmarks;
  InputImage? inputImage;

  final _orientations = {
    DeviceOrientation.portraitUp: 0,
    DeviceOrientation.landscapeLeft: 90,
    DeviceOrientation.portraitDown: 180,
    DeviceOrientation.landscapeRight: 270,
  };

  @override
  void initState() {
    super.initState();
    camera = cameras[isFrontCamera ? 1 : 0];
    final options = PoseDetectorOptions();
    poseDetector = PoseDetector(options: options);
    isFrontCamera = widget.query["isFrontCamera"] == "true" ? true : false;
    p2pConnection = P2PConnection(widget.query["ip"])..connect();
    initCamera();
  }

  void _sendMsg(StepType stepType, KeyType keyType) {
    currentStep = stepType;
    int keyCode = -1;
    switch (stepType) {
      case StepType.left:
        keyCode = 37;
        break;
      case StepType.down:
        keyCode = 40;
        break;
      case StepType.up:
        keyCode = 38;
        break;
      case StepType.right:
        keyCode = 39;
        break;
      default:
    }
    if (keyCode == -1) {
      return;
    }
    var map = {
      "openType": keyType.toString().split(".")[1],
      "keyCode": keyCode
    };
    p2pConnection!.send(map);
  }

  initCamera() async {
    controller = CameraController(camera, ResolutionPreset.medium,
        enableAudio: false,
        imageFormatGroup: Platform.isAndroid
            ? ImageFormatGroup.nv21 // for Android
            : ImageFormatGroup.bgra8888);
    controller.initialize().then((_) {
      if (!mounted) {
        return;
      }
      setState(() {});
      controller.startImageStream((CameraImage image) {
        onResults(image);
      });
    }).catchError((Object e) {
      if (e is CameraException) {
        switch (e.code) {
          case 'CameraAccessDenied':
            break;
          default:
            break;
        }
      }
    });
  }

  @override
  void dispose() {
    p2pConnection!.close();
    controller.stopImageStream();
    controller.dispose();
    poseDetector!.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    Size size = MediaQuery.of(context).size;
    if (!controller.value.isInitialized) {
      return Container();
    }
    return Scaffold(
      appBar: AppBar(title: Text(widget.query["ip"])),
      body: Stack(children: [
        CameraPreview(controller),
        landmarks != null
            ? Positioned(
                left: 0,
                top: 0,
                child: Transform(
                    alignment: Alignment.center,
                    transform: Matrix4.identity()..scale(-1.0, 1.0, 1.0),
                    child: CustomPaint(
                        size: size,
                        painter: DDRPainter2(
                            landmarks!,
                            inputImage!.metadata!.size.width,
                            inputImage!.metadata!.size.height,
                            currentStep,
                            stepAdapter))))
            : Container()
      ]),
    );
  }

  void onResults(CameraImage image) async {
    inputImage = _inputImageFromCameraImage(image);
    List<Pose> poses = await poseDetector!.processImage(inputImage!);
    landmarks = poses[0].landmarks;
    if (mounted) {
      setState(() {
      });
    }
    // print(poses![0].landmarks.length);
  }

  InputImage? _inputImageFromCameraImage(CameraImage image) {
    // get image rotation
    // it is used in android to convert the InputImage from Dart to Java
    // `rotation` is not used in iOS to convert the InputImage from Dart to Obj-C
    // in both platforms `rotation` and `camera.lensDirection` can be used to compensate `x` and `y` coordinates on a canvas
    final sensorOrientation = camera.sensorOrientation;
    InputImageRotation? rotation;
    if (Platform.isIOS) {
      rotation = InputImageRotationValue.fromRawValue(sensorOrientation);
    } else if (Platform.isAndroid) {
      var rotationCompensation =
          _orientations[controller!.value.deviceOrientation];
      if (rotationCompensation == null) return null;
      if (camera.lensDirection == CameraLensDirection.front) {
        // front-facing
        rotationCompensation = (sensorOrientation + rotationCompensation) % 360;
      } else {
        // back-facing
        rotationCompensation =
            (sensorOrientation - rotationCompensation + 360) % 360;
      }
      rotation = InputImageRotationValue.fromRawValue(rotationCompensation!);
    }
    if (rotation == null) return null;
    // get image format
    final format = InputImageFormatValue.fromRawValue(image.format.raw);
    // validate format depending on platform
    // only supported formats:
    // * nv21 for Android
    // * bgra8888 for iOS
    if (format == null ||
        (Platform.isAndroid && format != InputImageFormat.nv21) ||
        (Platform.isIOS && format != InputImageFormat.bgra8888)) return null;

    // since format is constraint to nv21 or bgra8888, both only have one plane
    if (image.planes.length != 1) return null;
    final plane = image.planes.first;
    // compose InputImage using bytes
    return InputImage.fromBytes(
      bytes: plane.bytes,
      metadata: InputImageMetadata(
        size: Size(image.width.toDouble(), image.height.toDouble()),
        rotation: rotation, // used only in Android
        format: format, // used only in iOS
        bytesPerRow: plane.bytesPerRow, // used only in iOS
      ),
    );
  }
}

class MyFootStepListener implements FootStepListener {
  final Function(StepType stepType, KeyType keyType) onStep;

  MyFootStepListener(this.onStep);

  @override
  void step(StepType stepType, KeyType keyType, Position position) {
    onStep(stepType, keyType);
  }
}
